﻿module internal FODATAC.HTTP

open System
open System.Net
open System.Linq
open Microsoft.Data.OData
open System.Collections.Generic
open System.Text.RegularExpressions
open System.Text
open System.Threading.Tasks

type ClientHttpResponseMessage(webResponse : HttpWebResponse) = 
    interface IODataResponseMessage with
        member this.Headers = 
            webResponse.Headers.AllKeys.Select(fun headerName -> KeyValuePair<string, string>(headerName, webResponse.Headers.Item(headerName)))
        
        member this.StatusCode 
            with get () = int webResponse.StatusCode
            and set (value : int) = raise (InvalidOperationException("The HTTP response is read-only, status code can't be modified on it."))
        member this.GetStream() =  webResponse.GetResponseStream()
        
        member this.GetHeader(headerName : string) = webResponse.Headers.Item(headerName)
        member this.SetHeader(headerName, headerValue) = 
            raise (InvalidOperationException("The HTTP response is read-only, headers can't be modified on it."))
    interface IODataResponseMessageAsync with
        member this.GetStreamAsync() =  Task.Factory.StartNew(fun () ->  webResponse.GetResponseStream() )
    interface IDisposable with
        member this.Dispose() =
            this.Dispose()
    member this.Dispose() =
        webResponse.GetResponseStream().Dispose()
        webResponse.Dispose()
        

type ClientHttpRequestMessage(requestUrl : Uri, nc) = 
    let webRequest = WebRequest.Create(requestUrl) :?> HttpWebRequest
    
    do 
        webRequest.Method <- ODataConstants.MethodGet
        webRequest.Credentials <- nc
        
    interface IODataRequestMessageAsync with
        
        member this.Method 
            with get () = webRequest.Method 
            and set (value) = 
                if String.IsNullOrEmpty(value) then raise (ArgumentException("Method must be a non-empty string.", "value"))
                else webRequest.Method <- value
        
        member this.Url 
            with get () = webRequest.RequestUri
            and set (value : Uri) = 
                raise (InvalidOperationException("The request's Url property can't be modified, it has to be specified in the constructor."))
        
        member this.Headers = 
            webRequest.Headers.AllKeys.Select(fun headerName -> KeyValuePair<string, string>(headerName, webRequest.Headers.Item(headerName)))
        member this.GetHeader(headerName : string) = webRequest.Headers.Item(headerName)
        member this.GetStreamAsync() =  Task<System.IO.Stream>.Factory.FromAsync(webRequest.BeginGetRequestStream(null,null), webRequest.EndGetRequestStream, TaskCreationOptions.None)
        member this.GetStream() =  Task<System.IO.Stream>.Factory.FromAsync(webRequest.BeginGetRequestStream(null,null), webRequest.EndGetRequestStream, TaskCreationOptions.None).Result
       
        member this.SetHeader(headerName, headerValue) = 
            let (|InvariantEqual|_|) (str : string) arg = 
                if String.Compare(str, arg, StringComparison.OrdinalIgnoreCase) = 0 then Some()
                else None
            match headerName with
            | InvariantEqual "Accept" when 1 = 2 -> webRequest.Accept <- headerValue
            | InvariantEqual "Content-Type" -> webRequest.ContentType <- headerValue
            | _ -> webRequest.Headers.Item(headerName)<-  headerValue
    
    static member private CreateODataErrorExceptionMessage(error : ODataError) = 
        let errorMessage = new StringBuilder(error.Message)
        let mutable innerError : ODataInnerError = error.InnerError
        while (innerError <> null) do
            errorMessage.AppendLine() |> ignore
            errorMessage.Append(innerError.Message) |> ignore
            innerError <- innerError.InnerError
        errorMessage.ToString()
   
    
    member this.GetResponseAsync() = 
        async{
                let mutable ex = Exception()
          //  try 
                let! response = webRequest.AsyncGetResponse()
                return new ClientHttpResponseMessage(response :?> HttpWebResponse)
//            with :? System.Net.WebException as webException ->  ex <- webException

            
            (*
                if webException.Response <> null then 
                    // If there is an error response, try to read it.
                    let errorResponseMessage : IODataResponseMessage = 
                        ClientHttpResponseMessage(webException.Response :?> HttpWebResponse) :> IODataResponseMessage
                    use messageReader = new ODataMessageReader(errorResponseMessage)
                    if messageReader.DetectPayloadKind()
                                    .Any(fun payloadKindDetectionResult -> payloadKindDetectionResult.PayloadKind = ODataPayloadKind.Error) then 
                        let error = messageReader.ReadError()
                        raise (ODataErrorException(ClientHttpRequestMessage.CreateODataErrorExceptionMessage(error), error))
                    else reraise()
                else reraise()            
                *)
        }
